package procilege.impl.formatter;

import java.util.HashMap;
import java.util.Map;

import procilege.compute.ExprFormatTraveller;
import procilege.compute.ExprFormatter;
import procilege.compute.PlaceTypeMetadata;
import procilege.compute.PlaceType;
import procilege.compute.TravelMap;

import com.google.inject.Inject;
import com.google.inject.Injector;

public class ExprFormatterImpl implements ExprFormatter,
		TravelMap<ExprFormatTraveller> {

	@Inject
	private Injector injector;

	private Map<Class<?>, PlaceType<ExprFormatTraveller>> map;

	public void format(StringBuilder sb, Object expr) {

		ExprFormatTravellerImpl traveller = new ExprFormatTravellerImpl(sb,
				this);

		traveller.setout(expr);
	}

	public String toString(Object expr) {

		StringBuilder sb = new StringBuilder();

		format(sb, expr);

		return sb.toString();
	}

	public synchronized void configure() {

		map = new HashMap<Class<?>, PlaceType<ExprFormatTraveller>>();

		register(BoolExprAndFormatter.class);
		register(BoolExprOrFormatter.class);
		register(EntityExprAndFormatter.class);
		register(EntityExprOrFormatter.class);
		register(NameFormatter.class);
		register(PredicateEntityFormatter.class);
		register(PredicateEntityPropertyFormatter.class);
		register(PredicateFunctionFormatter.class);
		register(PredicateObjectPropertyFormatter.class);
		register(PredicatePrivAvailiabilityFormatter.class);
		register(PredicateValueFormatter.class);
		register(PrivExprCompositionFormatter.class);
		register(PrivExprMergenceFormatter.class);
		register(PrivFormTermFormatter.class);
		register(RelationalExprFormatter.class);
		register(RelationConformFormatter.class);
		register(ExprSubstituteFormatter.class);
		register(LocalNameFormatter.class);
		register(SubstitutionFormatter.class);
		register(OpenBitSetFormatter.class);
	}

	private void register(Class<? extends PlaceType<ExprFormatTraveller>> oprcls) {

		PlaceTypeMetadata ot = oprcls.getAnnotation(PlaceTypeMetadata.class);
		Class<?> target = ot.type();
		try {
			PlaceType<ExprFormatTraveller> op = oprcls.newInstance();
			injector.injectMembers(op);
			map.put(target, op);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public PlaceType<ExprFormatTraveller> find(Class<?> cls) {

		if (map == null) {
			configure();
		}

		return map.get(cls);
	}
}
